const TetrisAI = {
  init(TETRIS_MAP_WIDTH_MAX, TETRIS_MAP_HEIGHT_MAX, TETRIS_ANGLE_MAX, TETRIS_DOTS_MAX, TETRIS_BLOCK) {
    this.TETRIS_MAP_WIDTH_MAX = TETRIS_MAP_WIDTH_MAX
    this.TETRIS_MAP_HEIGHT_MAX = TETRIS_MAP_HEIGHT_MAX
    this.TETRIS_ANGLE_MAX = TETRIS_ANGLE_MAX
    this.TETRIS_DOTS_MAX = TETRIS_DOTS_MAX
    this.TETRIS_BLOCK = TETRIS_BLOCK
  },
  evaluate(combo, t_map) {
    // 1.每列高度总和
    let column_total_height = 0

    // 3.空洞个数
    let hole_count = 0

    let top_height = 0
    const arr_heights = []
    for (let x = 0; x < this.TETRIS_MAP_WIDTH_MAX; x++) {
      let column_height = 0
      let is_column_started = false
      for (let y = this.TETRIS_MAP_HEIGHT_MAX + this.TETRIS_DOTS_MAX - 2; y >= 0; y--) {
        if (t_map[y][x] === 0) {
          if (is_column_started) {
            hole_count++
          }
        } else {
          if (!is_column_started) {
            column_height = y + 1
            is_column_started = true
          }
        }
      }

      if (top_height < column_height) top_height = column_height

      arr_heights.push(column_height)
      column_total_height += column_height
    }

    // 2.消除行数
    let removed_lines = 0
    for (let y = 0; y < this.TETRIS_MAP_HEIGHT_MAX + this.TETRIS_DOTS_MAX - 1; y++) {
      let is_full_line = true
      let is_empty_line = true
      for (let x = 0; x < this.TETRIS_MAP_WIDTH_MAX; x++) {
        if (t_map[y][x] === 0) {
          is_full_line = false
          if (!is_empty_line) {
            break
          }
        } else {
          is_empty_line = false
          if (!is_full_line) {
            break
          }
        }
      }

      if (is_full_line) {
        removed_lines++
      }

      if (is_empty_line) {
        break
      }
    }

    // 4.相邻列高度差值的绝对值的总和
    let bumpiness = 0
    for (let i = 0; i < arr_heights.length - 1; i++) {
      bumpiness += Math.abs(arr_heights[i] - arr_heights[i + 1])
    }

    const factors = [
      [ -0.510066, 0.760666, -0.35663, -0.184483, 7.7 ],
      [ -0.510066, -0.17, -0.35663, -0.154483, 7.7 ]
    ]

    const score =
      factors[0][0] * column_total_height +
      factors[0][1] * removed_lines +
      factors[0][2] * hole_count +
      factors[0][3] * bumpiness

    return score
  },
  checkCanPut(x, y, angle, type, t_map) {
    let is_float = true
    const c_pos = this.TETRIS_BLOCK[type * this.TETRIS_ANGLE_MAX + angle]
    for (let n = 0; n < this.TETRIS_DOTS_MAX * 2; n += 2) {
      const c_x = x + c_pos[n]
      const c_y = y + c_pos[n + 1]
      if (c_y >= this.TETRIS_MAP_HEIGHT_MAX + this.TETRIS_DOTS_MAX - 2) {
        return 1
      }

      if (c_y < 0) {
        return 2
      }

      if (c_x < 0 || c_x >= this.TETRIS_MAP_WIDTH_MAX) {
        return 3
      }

      if (t_map[c_y][c_x] !== 0) {
        return 4
      }

      if (c_y === 0 || t_map[c_y - 1][c_x] !== 0) {
        is_float = false
      }
    }

    if (is_float) {
      return 5
    }

    return 0
  },
  checkX(x, y, angle, type, t_map) {
    const c_pos = this.TETRIS_BLOCK[type * this.TETRIS_ANGLE_MAX + angle]
    for (let n = 0; n < this.TETRIS_DOTS_MAX * 2; n += 2) {
      const c_x = x + c_pos[n]
      const c_y = y + c_pos[n + 1]
      if (c_x < 0 || c_x >= this.TETRIS_MAP_WIDTH_MAX || t_map[c_y][c_x] !== 0) {
        return false
      }
    }

    return true
  },
  canMoveLeft(x, y, angle, type, t_map) {
    const c_pos = this.TETRIS_BLOCK[type * this.TETRIS_ANGLE_MAX + angle]
    for (let n = 0; n < this.TETRIS_DOTS_MAX * 2; n += 2) {
      const c_x = x + c_pos[n]
      const c_y = y + c_pos[n + 1]
      if (!(c_x >= 0 && t_map[c_y][c_x] === 0)) {
        return false
      }
    }

    return true
  },
  canMoveDown(x, y, angle, type, t_map) {
    const c_pos = this.TETRIS_BLOCK[type * this.TETRIS_ANGLE_MAX + angle]
    for (let n = 0; n < this.TETRIS_DOTS_MAX * 2; n += 2) {
      const c_x = x + c_pos[n]
      const c_y = y + c_pos[n + 1]
      if (!(c_y >= 0 && t_map[c_y][c_x] === 0)) {
        return false
      }
    }

    return true
  },
  _getBestPos(combo, cur_map, types, index) {
    let best_score = -10000.0
    let best_pos = null
    const cur_type = types[index]

    for (let angle = this.TETRIS_ANGLE_MAX - 1; angle >= 0; angle--) {
      for (let x = 0; x < this.TETRIS_MAP_WIDTH_MAX; x++) {
        if (!this.checkX(x, this.TETRIS_MAP_HEIGHT_MAX - 1, angle, cur_type, cur_map)) {
          continue
        }

        let y = this.TETRIS_MAP_HEIGHT_MAX - 1
        while (this.canMoveDown(x, y, angle, cur_type, cur_map)) y--
        y++

        const ret = this.checkCanPut(x, y, angle, cur_type, cur_map)
        if (ret === 0) {
          // 复制临时地图
          const t_map = new Array(this.TETRIS_MAP_HEIGHT_MAX + this.TETRIS_DOTS_MAX - 1)
          for (let t_y = 0; t_y < this.TETRIS_MAP_HEIGHT_MAX + this.TETRIS_DOTS_MAX - 1; t_y++) {
            t_map[t_y] = new Array(this.TETRIS_MAP_WIDTH_MAX)
            for (let t_x = 0; t_x < this.TETRIS_MAP_WIDTH_MAX; t_x++) {
              t_map[t_y][t_x] = cur_map[t_y][t_x]
            }
          }

          // 把方块应用到临时地图
          const c_pos = this.TETRIS_BLOCK[cur_type * this.TETRIS_ANGLE_MAX + angle]
          for (let n = 0; n < this.TETRIS_DOTS_MAX * 2; n += 2) {
            const c_x = x + c_pos[n]
            const c_y = y + c_pos[n + 1]
            t_map[c_y][c_x] = cur_type + 1
          }

          let score = -10000.0
          if (index === types.length - 1) { // 评估最后一个方块放置后的得分
            score = this.evaluate(combo, t_map)
          } else { // 评估下一方块放置后的得分
            score = this._getBestPos(combo, t_map, types, index + 1).m_score
          }

          if (score > best_score) {
            best_score = score
            best_pos = {
              'x': x,
              'y': y,
              'angle': angle
            }
          }
        }
      }
    }

    return {
      'm_score': best_score,
      'm_pos': best_pos
    }
  },
  getBestPos(combo, cur_map, types) {
    return this._getBestPos(combo, cur_map, types, 0)
  }
}

export default TetrisAI
